In [47]:
## STAT 415/615 Regression (M. Baron)
## Python Lab 3. Univariate Linear Regression
import os
import pandas as pd
import matplotlib.pyplot as plt
import statsmodels.api as sm
# Set the working directory. Yours will be different from mine. It should be the folder where you saved the data file from Blackboard.
os.chdir(r"C:\Users\baron\Documents\Teach\615 Regression\Data")
# Read the data:
H = pd.read_csv("HOME_SALES.csv")
# To see the names of all variables in the data set:
H.columns
Out[47]:
Index(['ID', 'SALES_PRICE', 'FINISHED_AREA', 'BEDROOMS', 'BATHROOMS',
'GARAGE_SIZE', 'YEAR_BUILT', 'STYLE', 'LOT_SIZE', 'AIR_CONDITIONER',
'POOL', 'QUALITY', 'HIGHWAY'],
dtype='object')
In [48]:
# Plot sales price against finished area:
plt.scatter(H["FINISHED_AREA"], H["SALES_PRICE"])
plt.xlabel("Finished Area")
plt.ylabel("Sales Price")
plt.show()
In [62]:
# That's familiar stuff so far. Now, we are fitting a regression model that we can use to predict
# the house sales price based on its area. So, X = area, Y = price.
# `OLS` – conducts regression analysis and estimates the regression slope and intercept
# `add_constant` – adds the intercept term to the regression model
# `fittedvalues` – computes predicted values based on the obtained regression equation
# `plot` – plots these predicted values
# Fit the regression model:
X = sm.add_constant(H["FINISHED_AREA"])
reg = sm.OLS(H["SALES_PRICE"], X).fit()
# The estimated regression line can be added to the scatterplot:
plt.scatter(H["FINISHED_AREA"], H["SALES_PRICE"])
plt.plot(
H["FINISHED_AREA"],
reg.fittedvalues,
linewidth=3
)
plt.xlabel("Finished Area")
plt.ylabel("Sales Price")
plt.show()
In [63]:
# The regression equation can also be obtained directly from the fitted model:
reg.params
Out[63]:
const -81.432946 FINISHED_AREA 0.158950 dtype: float64
In [64]:
# Prediction. Predict the price for three houses that have the finished area of 2500, 4000, and 6000 square feet.
# Create a new data frame containing the three values of finished area:
new_houses = pd.DataFrame({
"FINISHED_AREA": [2500, 4000, 6000]
})
# Add the intercept column:
new_houses = sm.add_constant(new_houses)
# Compute the predicted prices:
reg.predict(new_houses)
Out[64]:
0 315.942631 1 554.367977 2 872.268438 dtype: float64
In [65]:
# Inference. Use `summary()` to see results of the regression analysis.
# In Python, the equivalent of R's `summary(reg)` is:
print(reg.summary())
OLS Regression Results
==============================================================================
Dep. Variable: SALES_PRICE R-squared: 0.672
Model: OLS Adj. R-squared: 0.671
Method: Least Squares F-statistic: 1063.
Date: Sat, 29 Aug 2026 Prob (F-statistic): 8.28e-128
Time: 22:09:42 Log-Likelihood: -3021.3
No. Observations: 522 AIC: 6047.
Df Residuals: 520 BIC: 6055.
Df Model: 1
Covariance Type: nonrobust
=================================================================================
coef std err t P>|t| [0.025 0.975]
---------------------------------------------------------------------------------
const -81.4329 11.552 -7.049 0.000 -104.127 -58.739
FINISHED_AREA 0.1590 0.005 32.605 0.000 0.149 0.169
==============================================================================
Omnibus: 145.431 Durbin-Watson: 1.358
Prob(Omnibus): 0.000 Jarque-Bera (JB): 499.032
Skew: 1.267 Prob(JB): 4.33e-109
Kurtosis: 7.065 Cond. No. 7.90e+03
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 7.9e+03. This might indicate that there are
strong multicollinearity or other numerical problems.
In [66]:
# The regression output includes:
# Estimates of the intercept and slope
# Standard errors
# t-statistics
# p-values
# R-squared and adjusted R-squared
# Residual standard error
# F-statistic and its p-value
# Conclusion: the sample regression equation is Price = -81.4 + 0.159(area).
# The slope and the intercept are both significant. The area can actually be used as an important factor to predict the sales price. This variable alone explains 67.15% of the total variation of house sales prices.
In [67]:
# Analysis of Variance
# The ANOVA table for the regression can be obtained from the fitted model:
sm.stats.anova_lm(reg, typ=1)
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[67], line 5 1 # Analysis of Variance 2 3 # The ANOVA table for the regression can be obtained from the fitted model: ----> 5 sm.stats.anova_lm(reg, typ=1) File ~\AppData\Local\anaconda3\Lib\site-packages\statsmodels\stats\anova.py:353, in anova_lm(*args, **kwargs) 351 if len(args) == 1: 352 model = args[0] --> 353 return anova_single(model, **kwargs) 355 if typ not in [1, "I"]: 356 raise ValueError("Multiple models only supported for type I. " 357 "Got type %s" % str(typ)) File ~\AppData\Local\anaconda3\Lib\site-packages\statsmodels\stats\anova.py:70, in anova_single(model, **kwargs) 67 nobs = exog.shape[0] 69 response_name = model.model.endog_names ---> 70 design_info = model.model.data.design_info 71 exog_names = model.model.exog_names 72 # +1 for resids AttributeError: 'PandasData' object has no attribute 'design_info'
In [68]:
# What's the problem? `anova_lm()` expects a model created using the formula interface.
# Therefore, for the ANOVA table, we will fit the same model using `statsmodels`' formula interface:
import statsmodels.formula.api as smf
reg_formula = smf.ols(
"SALES_PRICE ~ FINISHED_AREA",
data=H
).fit()
# Now obtain the ANOVA table:
sm.stats.anova_lm(reg_formula)
# The ANOVA table contains:
# Degrees of freedom (`df`)
# Sum of squares (`sum_sq`)
# Mean squares (`mean_sq`)
# F-statistic (`F`)
# p-value (`PR(>F)`)
# The ANOVA F-statistic is the same F-statistic reported in the regression summary.
# For a simple linear regression with one predictor, the test of the regression slope
# and the overall F-test are equivalent.
Out[68]:
| df | sum_sq | mean_sq | F | PR(>F) | |
|---|---|---|---|---|---|
| FINISHED_AREA | 1.0 | 6.655486e+06 | 6.655486e+06 | 1063.103043 | 8.284610e-128 |
| Residual | 520.0 | 3.255426e+06 | 6.260434e+03 | NaN | NaN |